<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DevOps 流水线配置</title>
    <script type="text/javascript">
        mxLanguage = 'zh';
        var mxLoadStylesheets = false;
        mxBasePath = '../../../../../mxgraph/plugins';
        RESOURCE_BASE = '../../../../../mxgraph/plugins/resources/editor_zh';
    </script>
    <script src="../../../../../mxgraph/plugins/js/mxClient.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            height: 100vh;
            overflow: hidden;
        }
        
        #toolbar {
            padding: 10px;
            border-bottom: 1px solid #ccc;
            background-color: #f5f5f5;
        }
        
        #graphContainer {
            width: 100%;
            height: calc(100vh - 60px);
            position: relative;
            overflow: hidden;
        }
        
        .button {
            padding: 8px 16px;
            margin-right: 10px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        
        .button:hover {
            background-color: #0056b3;
        }
        
        .button:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }
    </style>
</head>
<body>
    <div id="toolbar">
        <button id="saveBtn" class="button">保存流水线</button>
        <button id="resetBtn" class="button">重置</button>
        <span id="status">就绪</span>
    </div>
    <div id="graphContainer"></div>

    <script>
        // 初始化 mxGraph
        var graphContainer = document.getElementById("graphContainer");
        var graph = new mxGraph(graphContainer);
        
        // 节点数据模型，用于跟踪节点的层级关系
        var nodeHierarchy = {};
        
        // 配置图形基本设置
        graph.setPanning(true);
        graph.panningHandler.useLeftButtonForPanning = true;
        graph.setAllowDanglingEdges(false);
        graph.connectionHandler.select = false;
        graph.view.setTranslate(20, 20);
        
        // 禁用节点直接拖拽功能，强制通过按钮操作节点
        graph.setCellsMovable(false);
        
        // 启用框选功能
        new mxRubberband(graph);
        
        // 自定义节点样式类
        function CustomShape() {
            mxShape.apply(this, arguments);
        }
        mxUtils.extend(CustomShape, mxShape);
        
        // 重写paintVertexShape方法以实现自定义渐变效果
        CustomShape.prototype.paintVertexShape = function(c, x, y, w, h) {
            // 获取节点样式中的fillColor
            var fillColor = this.style.fillColor;
            
            // 检查是否是特殊节点（开始/结束节点）- 有特殊颜色标识的节点保持原填充色
            var isSpecialNode = false;
            if (fillColor === '#4CAF50' || fillColor === '#f44336') {
                isSpecialNode = true;
            }
            
            // 创建矩形
            c.begin();
            c.rect(x, y, w, h);
            
            if (isSpecialNode) {
                // 特殊节点 - 使用原始填充色
                c.fillAndStroke();
            } else {
                // 普通节点 - 应用自定义渐变效果
                
                // 定义渐变 ID
                var gradientId = "gradient-" + this.state.cell.id;
                
                // 添加线性渐变定义
                var gradient = "<linearGradient id='" + gradientId + "' x1='0%' y1='0%' x2='0%' y2='100%'>" +
                    "<stop offset='0%' style='stop-color:#f9e8ff; stop-opacity:1' />" +
                    "<stop offset='100%' style='stop-color:rgb(252,252,255); stop-opacity:0.5' />" +
                    "</linearGradient>";
                
                // 将渐变添加到 <defs> 中
                var defs = c.root.ownerDocument.createElementNS(
                    "http://www.w3.org/2000/svg",
                    "defs"
                );
                defs.innerHTML = gradient;
                c.root.appendChild(defs);
                
                // 设置填充和边框
                c.stroke(); // 只绘制边框
                c.end(); // 结束当前上下文
                
                // 获取刚刚创建的矩形元素
                var rectElement = c.root.lastChild;
                
                // 设置矩形的 fill 属性为渐变的 URL
                rectElement.setAttribute("fill", "url(#" + gradientId + ")");
                
                return; // 已经处理完矩形，直接返回
            }
            
            c.end();
            
            // 获取矩形元素
            var rectElement = c.root.lastChild;
            
            // 设置圆角半径
            rectElement.setAttribute("rx", 16);
            rectElement.setAttribute("ry", 16);
        };
        
        // 注册自定义形状
        mxCellRenderer.registerShape("customize", CustomShape);
        
        // 设置默认样式
        var defaultStyle = graph.getStylesheet().getDefaultVertexStyle();
        defaultStyle[mxConstants.STYLE_SHAPE] = "customize"; // 使用自定义形状
        defaultStyle[mxConstants.STYLE_STROKECOLOR] = '#333';
        defaultStyle[mxConstants.STYLE_FONTCOLOR] = '#333';
        defaultStyle[mxConstants.STYLE_FONTSIZE] = 12;
        defaultStyle[mxConstants.STYLE_FONTSTYLE] = 1; // 粗体
        
        var edgeStyle = graph.getStylesheet().getDefaultEdgeStyle();
        edgeStyle[mxConstants.STYLE_STROKECOLOR] = '#333';
        edgeStyle[mxConstants.STYLE_STROKEWIDTH] = 2;
        
        // 删除图标
        var deleteImage = new mxImage('../../../../../mxgraph/plugins/images/overlays/forbidden.png', 16, 16);
        
        // 添加图标
        var addImage = new mxImage('../../../../../mxgraph/plugins/images/overlays/add.png', 24, 24);
         // 自定义表单
        function CustomData(value) {
            this.value = value;
        }
        var customData = new mxObjectCodec(new CustomData());
        customData.encode = function (enc, obj) {
            var node = enc.document.createElement('CustomData');
            mxUtils.setTextContent(node, JSON.stringify(obj));
            return node;
        };
        customData.decode = function (dec, node, into) {
            var obj = JSON.parse(mxUtils.getTextContent(node));
            obj.constructor = CustomData;
            return obj;
        };
        mxCodecRegistry.register(customData);
        // 重写创建控制元素的方法
        var mxCellRendererCreateControl = mxCellRenderer.prototype.createControl;
        mxCellRenderer.prototype.createControl = function (state) {
            mxCellRendererCreateControl.apply(this, arguments);
            
            var graph = state.view.graph;
            
            // 只对顶点添加删除按钮（排除开始和结束节点）
            if (graph.getModel().isVertex(state.cell) && 
                state.cell.id != 'start-node' && 
                state.cell.id != 'end-node') {
                if (state.deleteControl == null) {
                    var b = new mxRectangle(0, 0, deleteImage.width, deleteImage.height);
                    state.deleteControl = new mxImageShape(b, deleteImage.src);
                    state.deleteControl.dialect = graph.dialect;
                    state.deleteControl.preserveImageAspect = false;
                    
                    this.initControl(state, state.deleteControl, false, function (evt) {
                        if (graph.isEnabled()) {
                            // 删除节点及其所有子节点
                            var cellsToRemove = [state.cell];
                            
                            // 查找所有子节点
                            var findChildNodes = function(node) {
                                if (nodeHierarchy[node.id] && nodeHierarchy[node.id].children) {
                                    for (var i = 0; i < nodeHierarchy[node.id].children.length; i++) {
                                        var childId = nodeHierarchy[node.id].children[i];
                                        var childNode = graph.getModel().getCell(childId);
                                        if (childNode) {
                                            cellsToRemove.push(childNode);
                                            findChildNodes(childNode);
                                        }
                                    }
                                }
                            };
                            
                            findChildNodes(state.cell);
                            graph.removeCells(cellsToRemove);
                            
                            // 更新层级数据
                            for (var i = 0; i < cellsToRemove.length; i++) {
                                delete nodeHierarchy[cellsToRemove[i].id];
                            }
                            
                            // 重新布局
                            updateLayout();
                            
                            mxEvent.consume(evt);
                        }
                    });
                }
            } else if (state.deleteControl != null) {
                state.deleteControl.destroy();
                state.deleteControl = null;
            }
        };
        
        // 计算删除控件边界
        var getDeleteControlBounds = function (state) {
            if (state.deleteControl != null) {
                var oldScale = state.deleteControl.scale;
                var w = state.deleteControl.bounds.width / oldScale;
                var h = state.deleteControl.bounds.height / oldScale;
                var s = state.view.scale;
                
                return (state.view.graph.getModel().isEdge(state.cell)) ?
                    new mxRectangle(state.x + state.width / 2 - w / 2 * s,
                        state.y + state.height / 2 - h / 2 * s, w * s, h * s) :
                    new mxRectangle(state.x + state.width - w * s,
                        state.y, w * s, h * s);
            }
            
            return null;
        };
        
        // 重写重绘控制元素方法
        var mxCellRendererRedrawControl = mxCellRenderer.prototype.redrawControl;
        mxCellRenderer.prototype.redrawControl = function (state) {
            mxCellRendererRedrawControl.apply(this, arguments);
            
            if (state.deleteControl != null) {
                var bounds = getDeleteControlBounds(state);
                var s = state.view.scale;
                
                if (state.deleteControl.scale != s || !state.deleteControl.bounds.equals(bounds)) {
                    state.deleteControl.bounds = bounds;
                    state.deleteControl.scale = s;
                    state.deleteControl.redraw();
                }
            }
        };
        
        // 销毁控制元素
        var mxCellRendererDestroy = mxCellRenderer.prototype.destroy;
        mxCellRenderer.prototype.destroy = function (state) {
            mxCellRendererDestroy.apply(this, arguments);
            
            if (state.deleteControl != null) {
                state.deleteControl.destroy();
                state.deleteControl = null;
            }
        };
        
        // 添加边覆盖层（用于在线上添加新节点）
        var addEdgeOverlay = function (edge) {
            // 创建添加图标覆盖层
            var overlay = new mxCellOverlay(addImage, '在此处插入节点');
            overlay.cursor = 'hand';
            
            // 点击事件处理
            overlay.addListener(mxEvent.CLICK, function (sender, evt) {
                insertNodeOnEdge(edge);
            });
            
            // 设置覆盖层
            graph.addCellOverlay(edge, overlay);
        };
        
        // 在边上插入节点
        var insertNodeOnEdge = function (edge) {
            graph.getModel().beginUpdate();
            try {
                var source = edge.source;
                var target = edge.target;
                var parent = graph.getDefaultParent();
                
                // 获取源节点和目标节点的位置
                var sourceGeo = graph.getCellGeometry(source);
                var targetGeo = graph.getCellGeometry(target);
                
                // 计算新节点的位置（在源节点和目标节点之间）
                var newX = (sourceGeo.x + targetGeo.x) / 2;
                var newY = sourceGeo.y; // 保持在同一水平线上
                
                // 创建新节点 - 应用自定义样式
                var newNode = graph.insertVertex(
                    parent, 
                    null, 
                    '步骤 ' + getMainStepCount(), 
                    newX, 
                    newY, 
                    140, // 增加节点宽度，与updateLayout保持一致
                    60, // 增加节点高度，与updateLayout保持一致
                    'shape=customize;'
                );
                
                // 使用mxgraph默认的节点ID生成策略
                // 初始化节点层级数据（仅保留必要的层级信息）
                nodeHierarchy[newNode.id] = {
                    id: newNode.id,
                    parent: null,
                    children: [],
                    depth: 0,
                    isMainPath: true
                };
                
                // 保存基本信息到CustomData对象中
                newNode.data = new CustomData({
                    name: '步骤 ' + getMainStepCount(),
                    isMainPath: true,
                    depth: 0
                });
                
                // 添加节点右侧的串行步骤添加按钮
                // 禁用右侧+号按钮 - addSerialOverlay(newNode);
                
                // 添加节点下方的并行任务添加按钮
                addParallelOverlay(newNode);
                
                // 删除原来的边
                graph.removeCells([edge]);
                
                // 创建新的连接
                var newEdge1 = graph.insertEdge(parent, null, '', source, newNode);
                var newEdge2 = graph.insertEdge(parent, null, '', newNode, target);
                
                // 为新边添加覆盖层
                addEdgeOverlay(newEdge1);
                addEdgeOverlay(newEdge2);
                
                // 更新布局
                updateLayout();
            } finally {
                graph.getModel().endUpdate();
            }
        };
        
        // 生成节点ID（二维坐标系）
        var generateNodeId = function (parentNode, parallelIndex) {
            // 开始节点特殊处理
            if (!parentNode) {
                return '0';
            }
            
            // 获取父节点的nodeId
            var parentNodeId = nodeHierarchy[parentNode.id] ? nodeHierarchy[parentNode.id].nodeId : null;
            
            if (!parentNodeId) {
                // 如果父节点没有nodeId，这可能是首次初始化，返回'1'
                return '1';
            }
            
            // 判断这是主路径节点还是并行节点
            if (parallelIndex === null) {
                // 主路径节点 - 基于父节点的nodeId生成新的ID
                if (parentNodeId === '0') {
                    // 开始节点的下一个主路径节点是'1'
                    return '1';
                } else if (parentNodeId.includes('-')) {
                    // 如果父节点是并行节点，找到最近的主路径节点
                    var parts = parentNodeId.split('-');
                    var mainPathId = parts[0];
                    return (parseInt(mainPathId) + 1).toString();
                } else {
                    // 直接增加主路径编号
                    return (parseInt(parentNodeId) + 1).toString();
                }
            } else {
                // 并行节点 - 格式为 主路径ID-并行索引
                if (parentNodeId === '0') {
                    // 开始节点的并行节点
                    return '1-' + (parallelIndex + 1);
                } else if (!parentNodeId.includes('-')) {
                    // 主路径节点的并行节点
                    return parentNodeId + '-' + (parallelIndex + 1);
                } else {
                    // 并行节点的并行节点，保持相同的主路径ID
                    var parts = parentNodeId.split('-');
                    return parts[0] + '-' + (parallelIndex + 1);
                }
            }
        };
        
        // 添加串行步骤覆盖层（节点右侧边缘）
        var addSerialOverlay = function (cell) {
            // 创建添加图标覆盖层（位于节点右侧边缘）
            var overlay = new mxCellOverlay(addImage, '添加串行步骤');
            overlay.cursor = 'hand';
            
            // 点击事件处理
            overlay.addListener(mxEvent.CLICK, function (sender, evt) {
                addSerialStep(cell);
            });
            
            // 设置覆盖层位置在节点右侧边缘
            overlay.align = mxConstants.ALIGN_RIGHT;
            overlay.verticalAlign = mxConstants.ALIGN_MIDDLE;
            overlay.offset = new mxPoint(2, 0); // 紧贴节点右侧边缘
            
            // 设置覆盖层
            graph.addCellOverlay(cell, overlay);
        };
        
        // 添加并行任务覆盖层（节点下方边缘）
        var addParallelOverlay = function (cell) {
            // 创建添加图标覆盖层（位于节点下方边缘）
            var overlay = new mxCellOverlay(addImage, '添加并行任务');
            overlay.cursor = 'hand';
            
            // 点击事件处理
            overlay.addListener(mxEvent.CLICK, function (sender, evt) {
                addChildTask(cell);
            });
            
            // 设置覆盖层位置在节点下方边缘
            overlay.align = mxConstants.ALIGN_CENTER;
            overlay.verticalAlign = mxConstants.ALIGN_BOTTOM;
            overlay.offset = new mxPoint(0, 2); // 紧贴节点下方边缘
            
            // 设置覆盖层
            graph.addCellOverlay(cell, overlay);
        };
        
        // 获取主路径步骤数量
        var getMainStepCount = function () {
            var mainStepCount = 0;
            var cells = graph.getChildCells(graph.getDefaultParent());
            
            for (var i = 0; i < cells.length; i++) {
                var cell = cells[i];
                if (!cell.edge) { // 只计算节点
                    // 检查节点是否为主路径节点
                    if (nodeHierarchy[cell.id] && nodeHierarchy[cell.id].isMainPath) {
                        // 排除开始节点和结束节点
                        if (cell.id !== 'start-node' && cell.id !== 'end-node') {
                            mainStepCount++;
                        }
                    }
                }
            }
            
            return mainStepCount;
        };
        
        // 添加串行步骤
        var addSerialStep = function (sourceCell) {
            graph.getModel().beginUpdate();
            try {
                var geo = graph.getCellGeometry(sourceCell);
                var parent = graph.getDefaultParent();
                
                // 创建串行步骤节点（在源节点右侧）- 应用自定义样式
                var serialNode = graph.insertVertex(
                    parent, 
                    null, 
                    '步骤 ' + (getMainStepCount() + 1), 
                    geo.x + 250, // 在源节点右侧250px处，与updateLayout保持一致
                    geo.y, 
                    140, // 增加节点宽度，与updateLayout保持一致
                    60, // 增加节点高度，与updateLayout保持一致
                    'shape=customize;'
                );
                
                // 使用mxgraph默认的节点ID生成策略
                // 初始化节点层级数据（仅保留必要的层级信息）
                nodeHierarchy[serialNode.id] = {
                    id: serialNode.id,
                    parent: null,
                    children: [],
                    depth: 0,
                    isMainPath: true
                };
                
                // 保存基本信息到CustomData对象中
                serialNode.data = new CustomData({
                    name: '步骤 ' + (getMainStepCount() + 1),
                    isMainPath: true,
                    depth: 0
                });
                
                // 添加覆盖层
                // 禁用右侧+号按钮 - addSerialOverlay(serialNode);
                addParallelOverlay(serialNode);
                
                // 创建连接线
                var edge = graph.insertEdge(parent, null, '', sourceCell, serialNode);
                
                // 为新边添加覆盖层
                addEdgeOverlay(edge);
                
                // 更新布局
                updateLayout();
            } finally {
                graph.getModel().endUpdate();
            }
        };
        
        // 添加子任务（并行任务）
        var addChildTask = function (parentCell) {
            graph.getModel().beginUpdate();
            try {
                var geo = graph.getCellGeometry(parentCell);
                var parent = graph.getDefaultParent();
                
                // 确保父节点的层级数据存在
                if (!nodeHierarchy[parentCell.id]) {
                    nodeHierarchy[parentCell.id] = {
                        id: parentCell.id,
                        parent: null,
                        children: [],
                        depth: 0,
                        isMainPath: false
                    };
                }
                
                // 获取父节点深度
                var parentDepth = nodeHierarchy[parentCell.id].depth;
                var newDepth = parentDepth + 1;
                
                // 临时标签，稍后会更新为'并行任务+id'格式
                var taskLabel = '并行任务';
                
                // 确保children数组存在
                if (!nodeHierarchy[parentCell.id].children) {
                    nodeHierarchy[parentCell.id].children = [];
                }
                
                // 创建新的并行任务节点（只做并行连接，不影响主路径）- 应用自定义样式
                var newNode = graph.insertVertex(
                    parent, 
                    null, 
                    taskLabel, 
                    geo.x, // X轴与父节点对齐
                    geo.y + 180, // 纵向向下偏移，与updateLayout保持一致
                    140, // 增加节点宽度，与updateLayout保持一致
                    60, // 增加节点高度，与updateLayout保持一致
                    'shape=customize;'
                );
                
                // 更新任务标签为'并行任务+自身ID'格式
                updateTaskLabelWithId(newNode);
                
                // 记录父子关系
                nodeHierarchy[parentCell.id].children.push(newNode.id);
                
                // 初始化子节点的层级数据（并行任务，非主路径）
                nodeHierarchy[newNode.id] = {
                    id: newNode.id,
                    parent: parentCell.id,
                    children: [],
                    depth: newDepth,
                    isMainPath: false
                };
                
                // 保存基本信息到CustomData对象中
                newNode.data = new CustomData({
                    name: newNode.value,
                    isMainPath: false,
                    depth: newDepth
                });
                
                // 添加覆盖层，让新节点也能添加子任务
                addParallelOverlay(newNode);
                
                // 只创建从父节点到新并行任务节点的连接线
                // 重要：不修改任何主路径的连接关系
                var edgeToNewNode = graph.insertEdge(parent, null, '', parentCell, newNode);
                graph.getModel().setStyle(edgeToNewNode, 'strokeColor=#666;dashed=1;endArrow=none;');
                addEdgeOverlay(edgeToNewNode);
                
                // 修复层级关系
                fixHierarchy();
                
                // 更新布局，确保新节点插入后，所有节点位置正确
                updateLayout();
                
                // 添加延迟效果，让用户感觉这是真实的操作
                setTimeout(function() {
                    console.log('并行任务节点添加完成，布局已更新');
                }, 1000);
            } finally {
                graph.getModel().endUpdate();
            }
        };
        
        // 生成任务标签
        var generateTaskLabel = function (parentCell, index, depth) {
            // 这里只返回一个临时标签，在节点创建后会更新为'并行任务+自身ID'
            return '并行任务';
        };
        
        // 更新任务节点标签为'并行任务+自身ID'
        var updateTaskLabelWithId = function (taskNode) {
            if (taskNode && taskNode.id) {
                var newLabel = '并行任务' + taskNode.id;
                graph.getModel().setValue(taskNode, newLabel);
                
                // 同时更新CustomData中的name属性
                if (taskNode.data && typeof taskNode.data === 'object') {
                    taskNode.data.name = newLabel;
                }
            }
        };
        
        // 获取节点下的最大深度
        var getMaxDepthForNode = function(nodeId) {
            var maxDepth = 0;
            var node = nodeHierarchy[nodeId];
            
            if (!node || !node.children || node.children.length === 0) {
                return 0;
            }
            
            // 递归计算每个子节点的最大深度
            for (var i = 0; i < node.children.length; i++) {
                var childId = node.children[i];
                var childDepth = 1 + getMaxDepthForNode(childId);
                if (childDepth > maxDepth) {
                    maxDepth = childDepth;
                }
            }
            
            return maxDepth;
        };
        
        // 生成任务标签
        var getTaskLabel = function (parentCell, index) {
            // 检查是否是主路径节点
            var isMainPath = nodeHierarchy[parentCell.id] ? nodeHierarchy[parentCell.id].isMainPath : true;
            
            if (isMainPath) {
                // 主路径节点的子任务使用数字标签
                return '任务 ' + (index + 1);
            } else {
                // 非主路径节点的子任务使用字母标签
                return '子任务 ' + String.fromCharCode(65 + index);
            }
        };
        
        // 获取主路径步骤数量
        var getMainStepCount = function() {
            var count = 0;
            var model = graph.getModel();
            var cells = model.getChildVertices(model.getRoot());
            
            for (var i = 0; i < cells.length; i++) {
                var cell = cells[i];
                if (cell.id != 'start-node' && cell.id != 'end-node') {
                    if (nodeHierarchy[cell.id] && nodeHierarchy[cell.id].isMainPath) {
                        count++;
                    }
                }
            }
            
            return count;
        };
        
        // 获取并行任务数量
        var getParallelTaskCount = function() {
            var count = 0;
            var model = graph.getModel();
            var cells = model.getChildVertices(model.getRoot());
            
            for (var i = 0; i < cells.length; i++) {
                var cell = cells[i];
                if (cell.id != 'start-node' && cell.id != 'end-node') {
                    if (nodeHierarchy[cell.id] && !nodeHierarchy[cell.id].isMainPath) {
                        count++;
                    }
                }
            }
            
            return count;
        };
        
        // 更新布局，实现二维数组式结构
        var updateLayout = function() {
            graph.getModel().beginUpdate();
            try {
                var parent = graph.getDefaultParent();
                var model = graph.getModel();
                var cells = model.getChildVertices(parent);
                
                // 计算最大深度以确定画布大小
                calculateMaxDepth();
                
                // 首先找出主路径上的节点（深度为0的节点）
                var mainPathNodes = [];
                var startNode = null;
                var endNode = null;
                
                for (var i = 0; i < cells.length; i++) {
                    var cell = cells[i];
                    if (cell.id == 'start-node') {
                        startNode = cell;
                    } else if (cell.id == 'end-node') {
                        endNode = cell;
                    } else if (nodeHierarchy[cell.id] && nodeHierarchy[cell.id].isMainPath) {
                        mainPathNodes.push(cell);
                    }
                }
                
                // 按位置从左到右排序主路径节点
                mainPathNodes.sort(function(a, b) {
                    var geoA = graph.getCellGeometry(a);
                    var geoB = graph.getCellGeometry(b);
                    return geoA.x - geoB.x;
                });
                
                // 计算主路径节点的位置
                var x = 80;
                var y = 80;
                var nodeWidth = 140; // 增加节点宽度
                var nodeHeight = 60; // 增加节点高度
                var horizontalSpacing = 300; // 增加水平间距，使布局更舒适
                var verticalSpacing = 180; // 增加垂直间距，使布局更舒适
                
                // 设置开始节点位置
                if (startNode) {
                    model.setGeometry(startNode, new mxGeometry(x, y, 100, 60)); // 增加开始节点尺寸
                    x += horizontalSpacing; // 使用与主路径节点相同的水平间距，确保连接线长度一致
                }
                
                // 设置主路径节点位置
                for (var i = 0; i < mainPathNodes.length; i++) {
                    var node = mainPathNodes[i];
                    
                    // 计算该节点及其所有子节点需要的垂直空间
                    var verticalOffset = calculateVerticalOffset(node.id);
                    
                    // 设置主路径节点位置
                    model.setGeometry(node, new mxGeometry(x, y, nodeWidth, nodeHeight));
                       
                      // 处理该节点的所有子节点
                      if (nodeHierarchy[node.id] && nodeHierarchy[node.id].children) {
                          // 为每个子节点分配位置
                          var currentY = y + verticalSpacing; // 从父节点下方开始
                           
                          for (var j = 0; j < nodeHierarchy[node.id].children.length; j++) {
                              var childId = nodeHierarchy[node.id].children[j];
                              var childNode = model.getCell(childId);
                              if (childNode) {
                                  // 设置子节点位置，X轴与父节点对齐，Y轴使用累积的当前Y位置
                                  model.setGeometry(childNode, new mxGeometry(x, currentY, nodeWidth, nodeHeight));
                                   
                                  // 递归处理子节点的子节点
                                  positionChildNodes(childNode, x, currentY + verticalSpacing);
                                   
                                  // 累加当前Y位置，为下一个子节点留出空间
                                  currentY += verticalSpacing;
                              }
                          }
                      }
                      
                      x += horizontalSpacing;
                }
                
                // 设置结束节点位置
                if (endNode) {
                    model.setGeometry(endNode, new mxGeometry(x, y, 100, 60)); // 增加结束节点尺寸
                }
                
                // 重新连接节点
                reconnectEdges();
                
                // 根据层级设置节点样式，增强视觉区分度
                updateNodeStylesByHierarchy();
                
            } finally {
                graph.getModel().endUpdate();
            }
        };
        
        // 计算节点及其所有子节点需要的垂直偏移量
        var calculateVerticalOffset = function(nodeId) {
            var node = nodeHierarchy[nodeId];
            if (!node || !node.children || node.children.length === 0) {
                return 0;
            }
            
            // 计算该节点所有子树中最长的垂直链
            var maxOffset = 0;
            var verticalSpacing = 180; // 增加垂直间距，与updateLayout保持一致
            
            for (var i = 0; i < node.children.length; i++) {
                var childId = node.children[i];
                var childOffset = verticalSpacing + calculateVerticalOffset(childId);
                if (childOffset > maxOffset) {
                    maxOffset = childOffset;
                }
            }
            
            return maxOffset;
        };
        
        // 计算整个图的最大深度
        var calculateMaxDepth = function() {
            var maxDepth = 0;
            
            for (var id in nodeHierarchy) {
                if (nodeHierarchy[id].depth > maxDepth) {
                    maxDepth = nodeHierarchy[id].depth;
                }
            }
            
            return maxDepth;
        };
        
        // 获取指定节点及其所有子节点的最大深度
        var getMaxDepthForNode = function(nodeId) {
            var node = nodeHierarchy[nodeId];
            if (!node || !node.children || node.children.length === 0) {
                return 0;
            }
            
            var maxDepth = 0;
            for (var i = 0; i < node.children.length; i++) {
                var childId = node.children[i];
                var childMaxDepth = 1 + getMaxDepthForNode(childId);
                if (childMaxDepth > maxDepth) {
                    maxDepth = childMaxDepth;
                }
            }
            
            return maxDepth;
        };
        
        // 递归设置子节点位置
        var positionChildNodes = function(node, baseX, baseY) {
            var model = graph.getModel();
            var nodeWidth = 140; // 增加节点宽度，与updateLayout保持一致
            var verticalSpacing = 180; // 增加垂直间距，与updateLayout保持一致
            
            if (nodeHierarchy[node.id] && nodeHierarchy[node.id].children) {
                // 计算每个子节点的位置，确保正确的垂直偏移
                var currentY = baseY;
                
                for (var i = 0; i < nodeHierarchy[node.id].children.length; i++) {
                    var childId = nodeHierarchy[node.id].children[i];
                    var childNode = model.getCell(childId);
                    if (childNode) {
                        // 设置子节点位置，X轴与父节点对齐，Y轴使用累积的当前Y位置
                        model.setGeometry(childNode, new mxGeometry(baseX, currentY, nodeWidth, 60)); // 增加节点高度，与updateLayout保持一致
                        
                        // 更新节点的深度信息
                        if (!nodeHierarchy[childId].depth) {
                            nodeHierarchy[childId].depth = nodeHierarchy[node.id].depth + 1;
                        }
                        
                        // 递归处理下一级子节点，将当前子节点的Y位置 + verticalSpacing作为下一级的基准位置
                        positionChildNodes(childNode, baseX, currentY + verticalSpacing);
                        
                        // 累加当前Y位置，为下一个子节点留出空间
                        // 这样可以确保新节点插入时，后续节点能够正确向下偏移
                        currentY += verticalSpacing;
                    }
                }
            }
        };
        
        // 修复层级关系，确保所有节点都有正确的父子关系
        var fixHierarchy = function() {
            // 创建反向映射：父节点ID -> 子节点ID列表
            var reverseMap = {};
            
            // 收集所有子节点关系
            for (var id in nodeHierarchy) {
                var node = nodeHierarchy[id];
                if (node.parent) {
                    if (!reverseMap[node.parent]) {
                        reverseMap[node.parent] = [];
                    }
                    reverseMap[node.parent].push(id);
                }
            }
            
            // 更新每个节点的子节点列表
            for (var id in reverseMap) {
                if (nodeHierarchy[id]) {
                    nodeHierarchy[id].children = reverseMap[id];
                }
            }
        };
        
        // 更新节点层级数据
        var updateNodeHierarchy = function(nodeId, updates) {
            if (nodeHierarchy[nodeId]) {
                for (var key in updates) {
                    if (updates.hasOwnProperty(key)) {
                        nodeHierarchy[nodeId][key] = updates[key];
                    }
                }
                // 更新后修复层级关系
                fixHierarchy();
                // 更新布局
                updateLayout();
            }
        };
        
        // 判断两个节点是否是父子关系
        var isChildRelationship = function(parentId, childId) {
            var childNode = nodeHierarchy[childId];
            return childNode && childNode.parent === parentId;
        };
        
        // 计算节点的深度
        var calculateDepths = function() {
            for (var id in nodeHierarchy) {
                var node = nodeHierarchy[id];
                if (!node.depth) {
                    node.depth = calculateNodeDepth(id);
                }
            }
        };
        
        // 递归计算节点深度
        var calculateNodeDepth = function(nodeId) {
            var node = nodeHierarchy[nodeId];
            if (!node || !node.parent) {
                return 0; // 根节点或主路径节点深度为0
            }
            return 1 + calculateNodeDepth(node.parent);
        };
        
        // 根据层级设置不同的边框颜色
        var updateNodeStylesByHierarchy = function() {
            var model = graph.getModel();
            
            for (var id in nodeHierarchy) {
                var node = nodeHierarchy[id];
                var cell = model.getCell(id);
                
                if (cell && !node.isMainPath) {
                    // 统一的纵向节点样式，移除基于深度的颜色区分
                    var style = 'strokeColor=#666666;strokeWidth=1;';
                    
                    // 如果有子节点，添加特殊标记
                    if (node.children && node.children.length > 0) {
                        style += 'fontWeight=normal;';
                    }
                    
                    model.setStyle(cell, style);
                }
            }
        };
        
        // 删除节点及其所有子节点
        var deleteNodeAndChildren = function(nodeId) {
            graph.getModel().beginUpdate();
            try {
                var nodesToDelete = [];
                collectNodesToDelete(nodeId, nodesToDelete);
                
                // 删除收集到的所有节点
                for (var i = 0; i < nodesToDelete.length; i++) {
                    var node = nodesToDelete[i];
                    // 从层级数据中移除
                    delete nodeHierarchy[node.id];
                }
                
                // 从图中删除节点
                graph.removeCells(nodesToDelete);
                
                // 修复父节点的子节点列表
                fixParentChildrenList();
                
                // 更新布局
                updateLayout();
            } finally {
                graph.getModel().endUpdate();
            }
        };
        
        // 收集要删除的节点（包括所有子节点）
        var collectNodesToDelete = function(nodeId, nodesToDelete) {
            var node = graph.getModel().getCell(nodeId);
            if (node) {
                nodesToDelete.push(node);
                
                // 递归收集子节点
                var hierarchyNode = nodeHierarchy[nodeId];
                if (hierarchyNode && hierarchyNode.children) {
                    for (var i = 0; i < hierarchyNode.children.length; i++) {
                        collectNodesToDelete(hierarchyNode.children[i], nodesToDelete);
                    }
                }
            }
        };
        
        // 修复父节点的子节点列表
        var fixParentChildrenList = function() {
            // 重新构建层级关系
            fixHierarchy();
        };
        
        // 显示节点信息（用于调试）
        var showNodeInfo = function(nodeId) {
            var node = nodeHierarchy[nodeId];
            if (node) {
                console.log('节点信息:', {
                    id: node.id,
                    parent: node.parent,
                    children: node.children,
                    depth: node.depth,
                    isMainPath: node.isMainPath
                });
            }
        };
        
        // 导出层级结构为二维数组格式（用于调试）
        var exportHierarchyAsArray = function() {
            var result = [];
            
            // 找出所有主路径节点
            var mainPathNodes = [];
            var model = graph.getModel();
            var cells = model.getChildVertices(model.getDefaultParent());
            
            for (var i = 0; i < cells.length; i++) {
                var cell = cells[i];
                if (cell.id != 'start-node' && cell.id != 'end-node') {
                    var hierarchyNode = nodeHierarchy[cell.id];
                    if (hierarchyNode && hierarchyNode.isMainPath) {
                        mainPathNodes.push(cell);
                    }
                }
            }
            
            // 按X坐标排序主路径节点
            mainPathNodes.sort(function(a, b) {
                var geoA = graph.getCellGeometry(a);
                var geoB = graph.getCellGeometry(b);
                return geoA.x - geoB.x;
            });
            
            // 构建二维数组
            for (var i = 0; i < mainPathNodes.length; i++) {
                var mainNode = mainPathNodes[i];
                var row = [mainNode.value]; // 第一行是主路径节点
                
                // 收集该主路径节点的所有层级子节点
                var allChildren = collectAllChildren(mainNode.id);
                
                // 按层级组织子节点
                var maxDepth = 0;
                for (var j = 0; j < allChildren.length; j++) {
                    if (allChildren[j].depth > maxDepth) {
                        maxDepth = allChildren[j].depth;
                    }
                }
                
                // 创建层级数组
                for (var depth = 1; depth <= maxDepth; depth++) {
                    var depthRow = [];
                    for (var j = 0; j < allChildren.length; j++) {
                        if (allChildren[j].depth === depth) {
                            depthRow.push(allChildren[j].value);
                        }
                    }
                    row.push(depthRow);
                }
                
                result.push(row);
            }
            
            console.log('二维数组结构:', JSON.stringify(result, null, 2));
            return result;
        };
        
        // 收集节点的所有子节点
        var collectAllChildren = function(nodeId) {
            var result = [];
            var node = nodeHierarchy[nodeId];
            
            if (node && node.children) {
                for (var i = 0; i < node.children.length; i++) {
                    var childId = node.children[i];
                    var childNode = graph.getModel().getCell(childId);
                    var hierarchyNode = nodeHierarchy[childId];
                    
                    if (childNode && hierarchyNode) {
                        result.push({
                            id: childId,
                            value: childNode.value,
                            depth: hierarchyNode.depth
                        });
                        
                        // 递归收集子节点的子节点
                        var grandchildren = collectAllChildren(childId);
                        for (var j = 0; j < grandchildren.length; j++) {
                            result.push(grandchildren[j]);
                        }
                    }
                }
            }
            
            return result;
        };
        
        // 修改删除节点处理函数，支持级联删除
        graph.addListener(mxEvent.CELLS_REMOVED, function(sender, evt) {
            var cells = evt.getProperty('cells');
            
            if (cells && cells.length > 0) {
                for (var i = 0; i < cells.length; i++) {
                    var cell = cells[i];
                    if (cell.isVertex() && nodeHierarchy[cell.id]) {
                        // 检查是否需要级联删除子节点
                        if (nodeHierarchy[cell.id].children && nodeHierarchy[cell.id].children.length > 0) {
                            // 递归删除所有子节点
                            for (var j = 0; j < nodeHierarchy[cell.id].children.length; j++) {
                                var childId = nodeHierarchy[cell.id].children[j];
                                deleteNodeAndChildren(childId);
                            }
                        }
                        
                        // 删除当前节点的层级数据
                        delete nodeHierarchy[cell.id];
                        
                        // 更新所有引用该节点为父节点的子节点
                        for (var id in nodeHierarchy) {
                            if (nodeHierarchy[id].parent === cell.id) {
                                nodeHierarchy[id].parent = null;
                            }
                        }
                        
                        // 修复层级关系
                        fixHierarchy();
                        // 更新布局
                        updateLayout();
                    }
                }
            }
        });
        
        // 重新连接所有节点
        var reconnectEdges = function() {
            var parent = graph.getDefaultParent();
            var model = graph.getModel();
            var cells = model.getChildVertices(parent);
            
            // 首先删除所有现有的边
            var edges = model.getChildEdges(parent);
            for (var i = 0; i < edges.length; i++) {
                model.remove(edges[i]);
            }
            
            // 找出主路径节点
            var mainPathNodes = [];
            var startNode = null;
            var endNode = null;
            
            for (var i = 0; i < cells.length; i++) {
                var cell = cells[i];
                if (cell.id == 'start-node') {
                    startNode = cell;
                } else if (cell.id == 'end-node') {
                    endNode = cell;
                } else if (nodeHierarchy[cell.id] && nodeHierarchy[cell.id].isMainPath) {
                    mainPathNodes.push(cell);
                }
            }
            
            // 按位置排序
            mainPathNodes.sort(function(a, b) {
                var geoA = graph.getCellGeometry(a);
                var geoB = graph.getCellGeometry(b);
                return geoA.x - geoB.x;
            });
            
            // 连接主路径节点
            var prevNode = startNode;
            for (var i = 0; i < mainPathNodes.length; i++) {
                if (prevNode) {
                    // 使用graph.insertEdge而不是model.insertEdge
                    var edge = graph.insertEdge(parent, null, '', prevNode, mainPathNodes[i]);
                    addEdgeOverlay(edge);
                }
                prevNode = mainPathNodes[i];
            }
            
            // 连接到结束节点
            if (prevNode && endNode) {
                // 使用graph.insertEdge而不是model.insertEdge
                var edge = graph.insertEdge(parent, null, '', prevNode, endNode);
                addEdgeOverlay(edge);
            }
            
            // 连接所有子节点到其父节点，但不显示箭头，因为并行任务是同时执行的
            for (var i = 0; i < cells.length; i++) {
                var cell = cells[i];
                if (nodeHierarchy[cell.id] && nodeHierarchy[cell.id].parent) {
                    var parentNode = model.getCell(nodeHierarchy[cell.id].parent);
                    if (parentNode) {
                        // 使用graph.insertEdge而不是model.insertEdge
                        var edge = graph.insertEdge(parent, null, '', parentNode, cell);
                        // 设置虚线样式并且不显示箭头
                        model.setStyle(edge, 'strokeColor=#666;dashed=1;endArrow=none;');
                    }
                }
            }
        };
        
        // 初始化图表
        var initGraph = function() {
            var parent = graph.getDefaultParent();
            
            graph.getModel().beginUpdate();
            try {
                // 创建开始节点 - 使用自定义形状但保持绿色标识
                var startNode = graph.insertVertex(
                    parent, 
                    'start-node', 
                    '开始', 
                    0, 
                    0, 
                    80, 
                    40, 
                    'shape=customize;strokeColor=#000;fillColor=#4CAF50;fontColor=#fff;'
                );
                
                // 创建CustomData对象保存基本信息
                startNode.data = new CustomData({
                    name: '开始',
                    isMainPath: true,
                    depth: 0
                });
                
                // 在nodeHierarchy中添加开始节点信息
                nodeHierarchy[startNode.id] = {
                    id: startNode.id,
                    parent: null,
                    children: [],
                    depth: 0,
                    isMainPath: true
                };
                
                // 为开始节点添加串行步骤添加按钮
                // 禁用右侧+号按钮 - addSerialOverlay(startNode);
                
                // 创建结束节点 - 使用自定义形状但保持红色标识
                var endNode = graph.insertVertex(
                    parent, 
                    'end-node', 
                    '结束', 
                    400, 
                    0, 
                    80, 
                    40, 
                    'shape=customize;strokeColor=#000;fillColor=#f44336;fontColor=#fff;'
                );
                
                // 创建连接线
                var initialEdge = graph.insertEdge(parent, null, '', startNode, endNode);
                
                // 为连接线添加覆盖层
                addEdgeOverlay(initialEdge);
                
                // 更新布局
                updateLayout();
            } finally {
                graph.getModel().endUpdate();
            }
        };
        
        // 保存流水线
        document.getElementById('saveBtn').onclick = function() {
            var encoder = new mxCodec();
            var node = encoder.encode(graph.getModel());
            var xml = mxUtils.getXml(node);
            
            // 打印XML字符串
            console.log('XML字符串:', xml);
            
            // 保存到localStorage，使用try-catch确保安全
            try {
                localStorage.setItem('demo_flow_model', xml);
                localStorage.setItem('demo_node_hierarchy', JSON.stringify(nodeHierarchy));
            } catch (e) {
                console.error('保存到localStorage失败:', e);
            }
            
            // 创建节点ID与queueId的映射
            var nodeQueueIdMap = generateNodeQueueIdMap();
            console.log('节点ID与queueId映射:', nodeQueueIdMap);
            
            // 导出结构用于后台处理
            var flowStructure = exportFlowStructure();
            console.log('流水线完整结构:', flowStructure);
            
            // 显示保存成功消息
            document.getElementById('status').textContent = '流水线已保存';
            alert('流水线已保存，XML和映射关系已导出到控制台');
        };
        
        // 导出流水线结构（适合后台处理的格式）
        // 生成节点ID与queueId的映射关系
        var generateNodeQueueIdMap = function() {
            var map = {};
            var mainPathNodes = [];
            var parallelNodes = {};
            
            // 首先找出所有主路径节点并按深度排序
            for (var nodeId in nodeHierarchy) {
                var node = nodeHierarchy[nodeId];
                if (node.isMainPath) {
                    mainPathNodes.push({id: nodeId, depth: node.depth});
                }
            }
            
            // 按深度排序主路径节点
            mainPathNodes.sort(function(a, b) { return a.depth - b.depth; });
            
            // 为主路径节点分配queueId
            mainPathNodes.forEach(function(node, index) {
                // 格式：层级-1
                var level = index + 1;
                map[node.id] = level.toString() + '-1';
                
                // 找出该主路径节点的所有并行节点
                parallelNodes[node.id] = [];
            });
            
            // 找出所有并行节点并分配queueId
            var parallelIndexMap = {};
            
            for (var nodeId in nodeHierarchy) {
                // 如果已经映射过，跳过
                if (map[nodeId]) continue;
                
                var node = nodeHierarchy[nodeId];
                
                // 查找父节点
                if (node.parent) {
                    var parentNode = nodeHierarchy[node.parent];
                    if (parentNode && map[node.parent]) {
                        // 基于父节点的queueId生成子节点的queueId
                        var parentQueueId = map[node.parent];
                        var baseLevel = parentQueueId.split('-')[0];
                        
                        // 初始化并行索引
                        if (!parallelIndexMap[baseLevel]) {
                            parallelIndexMap[baseLevel] = 2; // 1留给主路径节点
                        }
                        
                        // 分配并行节点的queueId
                        map[nodeId] = baseLevel + '-' + parallelIndexMap[baseLevel];
                        parallelIndexMap[baseLevel]++;
                    }
                }
            }
            
            // 确保所有节点都被映射，包括没有父节点的孤立节点
            var isolatedCount = 1;
            for (var nodeId in nodeHierarchy) {
                if (!map[nodeId]) {
                    map[nodeId] = 'isolated-' + isolatedCount;
                    isolatedCount++;
                }
            }
            
            return map;
        };
        
        var exportFlowStructure = function() {
            // 收集所有节点信息
            var nodes = {};
            var edges = [];
            var mainPath = [];
            
            // 遍历所有节点
            for (var nodeId in nodeHierarchy) {
                var node = nodeHierarchy[nodeId];
                nodes[nodeId] = {
                    id: nodeId,
                    displayName: graph.getLabel(graph.getModel().getCell(nodeId)) || '未命名',
                    isMainPath: node.isMainPath,
                    depth: node.depth,
                    parentId: node.parent || null,
                    children: node.children
                };
                
                // 构建主路径
                if (node.isMainPath) {
                    mainPath.push(nodeId);
                }
            }
            
            // 构建完整结构
            return {
                nodes: nodes,
                mainPath: mainPath.sort(function(a, b) { return parseInt(a) - parseInt(b); }),
                nextMainNodeMap: buildNextMainNodeMap(nodes, mainPath) // 快速查找下一主节点
            };
        };
        
        // 构建下一主节点映射
        var buildNextMainNodeMap = function(nodes, mainPath) {
            var map = {};
            for (var i = 0; i < mainPath.length - 1; i++) {
                map[mainPath[i]] = mainPath[i + 1];
            }
            return map;
        };
        
        // 构建同级节点映射
        var buildSiblingMap = function(nodes, parallelGroups) {
            var map = {};
            for (var groupId in parallelGroups) {
                var siblings = parallelGroups[groupId];
                siblings.forEach(function(siblingId) {
                    map[siblingId] = siblings.filter(function(id) { return id !== siblingId; });
                });
            }
            return map;
        };
        
        // 重置图表
        document.getElementById('resetBtn').onclick = function() {
            if (confirm('确定要重置所有更改吗？')) {
                graph.getModel().clear();
                nodeHierarchy = {};
                
                // 清除localStorage
                try {
                    localStorage.removeItem('demo_flow_model');
                    localStorage.removeItem('demo_node_hierarchy');
                } catch (e) {
                    console.error('清除localStorage失败:', e);
                }
                
                initGraph();
                document.getElementById('status').textContent = '已重置';
            }
        };
        
        // 页面加载时尝试从localStorage恢复
        window.addEventListener('load', function() {
            try {
                var savedHierarchy = localStorage.getItem('demo_node_hierarchy');
                if (savedHierarchy) {
                    // 这里可以添加恢复逻辑，目前我们只初始化新图表
                    console.log('检测到保存的层级数据');
                }
            } catch (e) {
                console.error('从localStorage加载失败:', e);
            }
        });
        

        
        // 初始化
        initGraph();

    graph.addListener(mxEvent.CLICK, function (sender, evt) {
        const cell = evt.getProperty("cell");
        console.log('点击的单元格:', cell);
        if (cell && cell.data) {
            console.log('CustomData表单数据:', cell.data);
            // 打印具体的表单字段
            console.log('节点ID:', cell.data.nodeId);
            console.log('任务名称:', cell.data.name);
            console.log('是否为主路径:', cell.data.isMainPath);
            console.log('层级深度:', cell.data.depth);
        }
        evt.consume();
  });
    </script>
</body>
</html>